"""__init__.py

Adds an hero Flash builder to SCons.

"""

# License text

import SCons.Util
from SCons.Builder import (Builder, BuilderBase, CompositeBuilder,
                           DictCmdGenerator, EmitterProxy, DictEmitter,
                           ListEmitter, _null, _node_errors)
from SCons.Action import Action, CommandGeneratorAction
from SCons.Script import ARGUMENTS
from SCons.Errors import InternalError, UserError
import os
import sys
import re
import subprocess
from xml.dom import minidom
from xml.dom.minidom import Document
# debugging
import traceback

short_to_long = {
    '-accessible': '-compiler.accessible',
    '-actionscript-file-encoding': '-compiler.actionscript-file-encoding',
    '-allow-source-path-overlap': '-compiler.allow-source-path-overlap',
    '-as3': '-compiler.as3',
    '-context-root': '-compiler.context-root',
    '-debug': '-compiler.debug',
    '-defaults-css-files': '-compiler.defaults-css-files',
    '-defaults-css-url': '-compiler.defaults-css-url',
    '-define': '-compiler.define',
    '-es': '-compiler.es',
    '-el': '-compiler.external-library-path',
    '-advanced-anti-aliasing': '-compiler.fonts.advanced-anti-aliasing',
    '-flash-type': '-compiler.fonts.flash-type',
    '-language-range': '-compiler.fonts.languages.language-range',
    '-local-fonts-snapshot': '-compiler.fonts.local-fonts-snapshot',
    '-managers': '-compiler.fonts.managers',
    '-max-cached-fonts': '-compiler.fonts.max-cached-fonts',
    '-max-glyphs-per-face': '-compiler.fonts.max-glyphs-per-face',
    '-headless-server': '-compiler.headless-server',
    '-include-libraries': '-compiler.include-libraries',
    '-incremental': '-compiler.incremental',
    '-keep-all-type-selectors': '-compiler.keep-all-type-selectors',
    '-keep-as3-metadata': '-compiler.keep-as3-metadata',
    '-keep': '-compiler.keep-generated-actionscript',
    '-library-path': '-compiler.library-path',
    '-locale': '-compiler.locale',
    '-compatibility-version': '-compiler.mxml.compatibility-version',
    '-namespace': '-compiler.namespaces.namespace',
    '-optimize': '-compiler.optimize',
    '-services': '-compiler.services',
    '-show-actionscript-warnings': '-compiler.show-actionscript-warnings',
    '-show-binding-warnings': '-compiler.show-binding-warnings',
    '-show-shadowed-device-font-warnings': '-compiler.show-shadowed-device-font-warnings',
    '-show-unused-type-selector-warnings': '-compiler.show-unused-type-selector-warnings',
    '-sp': '-compiler.source-path',
    '-strict': '-compiler.strict',
    '-theme': '-compiler.theme',
    '-use-resource-bundle-metadata': '-compiler.use-resource-bundle-metadata',
    '-verbose-stacktraces': '-compiler.verbose-stacktraces',
    '-warn-array-tostring-changes': '-compiler.warn-array-tostring-changes',
    '-warn-assignment-within-conditional': '-compiler.warn-assignment-within-conditional',
    '-warn-bad-array-cast': '-compiler.warn-bad-array-cast',
    '-warn-bad-bool-assignment': '-compiler.warn-bad-bool-assignment',
    '-warn-bad-date-cast': '-compiler.warn-bad-date-cast',
    '-warn-bad-es3-type-method': '-compiler.warn-bad-es3-type-method',
    '-warn-bad-es3-type-prop': '-compiler.warn-bad-es3-type-prop',
    '-warn-bad-nan-comparison': '-compiler.warn-bad-nan-comparison',
    '-warn-bad-null-assignment': '-compiler.warn-bad-null-assignment',
    '-warn-bad-null-comparison': '-compiler.warn-bad-null-comparison',
    '-warn-bad-undefined-comparison': '-compiler.warn-bad-undefined-comparison',
    '-warn-boolean-constructor-with-no-args': '-compiler.warn-boolean-constructor-with-no-args',
    '-warn-changes-in-resolve': '-compiler.warn-changes-in-resolve',
    '-warn-class-is-sealed': '-compiler.warn-class-is-sealed',
    '-warn-const-not-initialized': '-compiler.warn-const-not-initialized',
    '-warn-constructor-returns-value': '-compiler.warn-constructor-returns-value',
    '-warn-deprecated-event-handler-error': '-compiler.warn-deprecated-event-handler-error',
    '-warn-deprecated-function-error': '-compiler.warn-deprecated-function-error',
    '-warn-deprecated-property-error': '-compiler.warn-deprecated-property-error',
    '-warn-duplicate-argument-names': '-compiler.warn-duplicate-argument-names',
    '-warn-duplicate-variable-def': '-compiler.warn-duplicate-variable-def',
    '-warn-for-var-in-changes': '-compiler.warn-for-var-in-changes',
    '-warn-import-hides-class': '-compiler.warn-import-hides-class',
    '-warn-instance-of-changes': '-compiler.warn-instance-of-changes',
    '-warn-internal-error': '-compiler.warn-internal-error',
    '-warn-level-not-supported': '-compiler.warn-level-not-supported',
    '-warn-missing-namespace-decl': '-compiler.warn-missing-namespace-decl',
    '-warn-negative-uint-literal': '-compiler.warn-negative-uint-literal',
    '-warn-no-constructor': '-compiler.warn-no-constructor',
    '-warn-no-explicit-super-call-in-constructor':
    '-compiler.warn-no-explicit-super-call-in-constructor',
    '-warn-no-type-decl': '-compiler.warn-no-type-decl',
    '-warn-number-from-string-changes': '-compiler.warn-number-from-string-changes',
    '-warn-scoping-change-in-this': '-compiler.warn-scoping-change-in-this',
    '-warn-slow-text-field-addition': '-compiler.warn-slow-text-field-addition',
    '-warn-unlikely-function-value': '-compiler.warn-unlikely-function-value',
    '-warn-xml-class-has-changed': '-compiler.warn-xml-class-has-changed',
    '-frame': '-frames.frame',
    '-ic': '-include-classes',
    '-if': '-include-file',
    '-in': '-include-namespaces',
    '-ir': '-include-resource-bundles',
    '-is': '-include-sources',
    '-license': '-licenses.license',
    '-contributor': '-metadata.contributor',
    '-creator': '-metadata.creator',
    '-date': '-metadata.date',
    '-description': '-metadata.description',
    '-language': '-metadata.language',
    '-localized-description': '-metadata.localized-description',
    '-localized-title': '-metadata.localized-title',
    '-publisher': '-metadata.publisher',
    '-title': '-metadata.title',
    '-o': '-output',
    '-rsl': '-runtime-shared-libraries',
    '-rslp': '-runtime-shared-library-path',
    '-static-rsls': '-static-link-runtime-shared-libraries' }

def partitionOn(chars, string):
    count = 0
    for c in string:
        if c in chars:
            return string[0:count], c, string[count + 1:]
        else:
            count = count + 1
    return string, '', ''

def optionValue(raw):
    in_string = False
    escaped = False
    for i in range(0, len(raw) - 1):
        a = raw[i]
        b = raw[i + 1]
        if a == ' ' and not in_string and b == '-':
            return raw[0:i], raw[i + 1:]
        elif a == '\\':
            escaped = not escaped
        elif a == '"' and not escaped:
            in_string = not in_string
    return raw, ''

def nextOption(raw, placeholder):
    for i in range(0, len(raw) - 1):
        a = raw[i]
        b = raw[i + 1]
        if a in [' ', '\t', '\r', '\n'] and b == '-':
            name, separator, value = partitionOn(
                [' ', '=', '\t', '\n', '\r', '+'], raw[i + 1:])
            value, raw = optionValue(value)
            placeholder['name'] = name
            placeholder['separator'] = separator
            placeholder['value'] = value
            placeholder['raw'] = raw
            return placeholder
    return None

def appendOption(option, existing):
    # TODO: hande += option separators properly
    # only append when option is either repeatable
    # or has this separator in front
    parts = option['name'].split('.')
    parts_len = len(parts)
    if parts_len > 1:
        for i in range(0, parts_len):
            p = parts[i]
            if i == parts_len - 1:
                try:
                    existing[p].expand(option['value'])
                except (KeyError, AttributeError):
                    existing[p] = [option['value']]
            else:
                try:
                    existing = existing[p]
                except KeyError:
                    existing[p] = {}
                    existing = existing[p]
    else:
        try:
            existing[option['name']].expand(option['value'])
        except (KeyError, AttributeError):
            existing[option['name']] = [option['value']]

def replaceOption(option, existing):
    parts = option['name'].split('.')
    parts_len = len(parts)
    if parts_len > 1:
        for i in range(0, parts_len):
            p = parts[i]
            if i == parts_len - 1:
                existing[p] = option['value']
            else:
                try:
                    existing = existing[p]
                except KeyError:
                    existing[p] = {}
                    existing = existing[p]
    else:
        existing[option['name']] = option['value']

options_table = {
    'swf-version': replaceOption,
    'target-player': replaceOption,
    'remove-unused-rsls': replaceOption,
    'static-link-runtime-shared-libraries': replaceOption,
    'compute-digest': replaceOption,
    'use-network': replaceOption,
    'verbose-stack-trace': replaceOption,
    'benchmark': replaceOption,
    'debug-password': replaceOption,
    'default-background-color': replaceOption,
    'default-frame-rate': replaceOption,
    'directory': replaceOption,
    'dump-config': replaceOption,
    'output': replaceOption,
    'raw-metadata': replaceOption,
    'warnings': replaceOption,

    'runtime-shared-libraries': appendOption,
    'rsls-paths': appendOption,
    'default-size': appendOption,
    'externs': appendOption,
    'frames': appendOption, 
    'resource-bundle-list': appendOption,
    'include-classes': appendOption,
    'include-namespaces': appendOption,
    'include-lookup-only': appendOption,
    'include-file': appendOption,

    'compiler.accessible': replaceOption,
    'compiler.actionscript-file-encoding': replaceOption,
    'compiler.allow-source-path-overlap': replaceOption,
    'compiler.as3': replaceOption,
    'compiler.context-root': replaceOption,
    'compiler.debug': replaceOption,
    'compiler.defaults-css-url': replaceOption,
    'compiler.es': replaceOption,
    'compiler.fonts.advanced-anti-aliasing': replaceOption,
    'compiler.fonts.flash-type': replaceOption,
    'compiler.fonts.local-fonts-snapshot': replaceOption,
    'compiler.fonts.max-cached-fonts': replaceOption,
    'compiler.headless-server': replaceOption,
    'compiler.incremental': replaceOption,
    'compiler.keep-all-type-selectors': replaceOption,
    'compiler.keep-generated-actionscript': replaceOption,
    'compiler.mxml.compatibility-version': replaceOption,
    'compiler.optimize': replaceOption,
    'compiler.services': replaceOption,
    'compiler.show-actionscript-warnings': replaceOption,
    'compiler.show-binding-warnings': replaceOption,
    'compiler.show-shadowed-device-font-warnings': replaceOption,
    'compiler.show-unused-type-selector-warnings': replaceOption,
    'compiler.strict': replaceOption,
    'compiler.theme': replaceOption,
    'compiler.use-resource-bundle-metadata': replaceOption,
    'compiler.verbose-stacktraces': replaceOption,
    'compiler.warn-array-tostring-changes': replaceOption,
    'compiler.warn-assignment-within-conditional': replaceOption,
    'compiler.warn-bad-array-cast': replaceOption,
    'compiler.warn-bad-bool-assignment': replaceOption,
    'compiler.warn-bad-date-cast': replaceOption,
    'compiler.warn-bad-es3-type-method': replaceOption,
    'compiler.warn-bad-es3-type-prop': replaceOption,
    'compiler.warn-bad-nan-comparison': replaceOption,
    'compiler.warn-bad-null-assignment': replaceOption,
    'compiler.warn-bad-null-comparison': replaceOption,
    'compiler.warn-bad-undefined-comparison': replaceOption,
    'compiler.warn-boolean-constructor-with-no-args': replaceOption,
    'compiler.warn-changes-in-resolve': replaceOption,
    'compiler.warn-class-is-sealed': replaceOption,
    'compiler.warn-const-not-initialized': replaceOption,
    'compiler.warn-constructor-returns-value': replaceOption,
    'compiler.warn-deprecated-event-handler-error': replaceOption,
    'compiler.warn-deprecated-function-error': replaceOption,
    'compiler.warn-deprecated-property-error': replaceOption,
    'compiler.warn-duplicate-argument-names': replaceOption,
    'compiler.warn-duplicate-variable-def': replaceOption,
    'compiler.warn-for-var-in-changes': replaceOption,
    'compiler.warn-import-hides-class': replaceOption,
    'compiler.warn-instance-of-changes': replaceOption,
    'compiler.warn-internal-error': replaceOption,
    'compiler.warn-level-not-supported': replaceOption,
    'compiler.warn-missing-namespace-decl': replaceOption,
    'compiler.warn-negative-uint-literal': replaceOption,
    'compiler.warn-no-constructor': replaceOption,
    'compiler.warn-no-explicit-super-call-in-constructor': replaceOption,
    'compiler.warn-no-type-decl': replaceOption,
    'compiler.warn-number-from-string-changes': replaceOption,
    'compiler.warn-scoping-change-in-this': replaceOption,
    'compiler.warn-slow-text-field-addition': replaceOption,
    'compiler.warn-unlikely-function-value': replaceOption,
    'compiler.warn-xml-class-has-changed': replaceOption,
    
    'compiler.define': appendOption,
    'compiler.defaults-css-files': appendOption, 
    'compiler.external-library-path': appendOption,
    'compiler.fonts.languages.language-range': appendOption,
    'compiler.fonts.managers': appendOption,
    'compiler.fonts.max-glyphs-per-face': appendOption,
    'compiler.include-libraries': appendOption,
    'compiler.keep-as3-metadata': appendOption,
    'compiler.library-path': appendOption,
    'compiler.locale': appendOption,
    'compiler.namespaces.namespace': appendOption,
    'compiler.source-path': appendOption,
    'frames.frame': appendOption,
    'include-classes': appendOption,
    'include-file': appendOption,
    'include-namespaces': appendOption,
    'include-resource-bundles': appendOption,
    'include-sources': appendOption,
    'licenses.license': appendOption,
    'metadata.contributor': appendOption,
    'metadata.creator': appendOption,
    'metadata.date': appendOption,
    'metadata.description': appendOption,
    'metadata.language': appendOption,
    'metadata.localized-description': appendOption,
    'metadata.localized-title': appendOption,
    'metadata.publisher': appendOption,
    'metadata.title': appendOption,
    'runtime-shared-library-path': appendOption,
    'static-link-runtime-shared-libraries': appendOption }

def setOption(option, existing):
    try:
        options_table[option['name']](option, existing)
    except KeyError:
        raise UserError('Unknown compiler option (%s)' % option['name'])

def normalizeCompierOptions(raw, existing = {}):
    print 'normalizing... %s' % raw
    if not raw[0] == ' ':
        raw = ' ' + raw
    option = { 'name': None, 'separator': None, 'value': None, 'raw': None }
    while nextOption(raw, option):
        print 'option: %s' % dict(option)
        raw = ' ' + option['raw']
        if short_to_long.has_key(option['name']):
            option['name'] = short_to_long[option['name']]
        option['name'] = option['name'][1:]
        setOption(option, existing)
        if not raw:
            break
    return existing

def subDirsContainFiles(directory, subdirectories, files):
    for f in files:
        for d in subdirectories:
            if os.path.isfile(os.path.join(directory, d, f)):
                return os.path.join(directory, d, f)
    return None

def hasFlashDevelopProjects(files):
    collected = []
    for f in files:
        # There are some other projects too... *.docproj perhaps more...
        if re.test('\\.as3proj', f, re.IGNORECASE):
            collected.append(f)
    return collected

def loadFBProject(props_file):
    xmldoc = minidom.parse(props_file).documentElement
    compiler = xmldoc.getElementsByTagName('compiler')[0]
    # there's a bunch more useful arguments, not supported yet
    # next in: support multiple applications and more library linking stuff
    return { 'main-app': xmldoc.getAttribute('mainApplicationPath'),
             'compiler-args': compiler.getAttribute('additionalCompilerArguments'),
             'out-folder': compiler.getAttribute('outputFolderPath'),
             'src-folder': compiler.getAttribute('sourceFolderPath') }

def loadFDProject(files):
    # Fat TODO: nothing done yet
    return None

def dictToXML(dictionary, name, doc):
    result = doc.createElement(name)
    node = None
    for key, value in dictionary.iteritems():
        node = doc.createElement(key.replace('_', '-'))
        node.appendChild(doc.createTextNode(str(value)))
        result.appendChild(node)
    return result

def makeMetadata(metadata, doc):
    return dictToXML(metadata, 'metadata', doc)

def makeLicenses(licenses, doc):
    return dictToXML(licenses, 'licenses', doc)

def makeSharedLibPath(library_info, doc):
    result = doc.createElement('runtime-shared-library-path')
    path_node = doc.createElement('path-element')
    path_node.appendChild(doc.createTextNode(library_info.path_element))
    node = None
    node_value = None
    for i in library_info.infos:
        node_value = i.get('rsl-url', None)
        if node_value:
            node = doc.createElement('rsl-url')
            node.appendChild(doc.createTextNode(node_value))
            result.appendChild(node)
        node_value = i.get('policy-file', None)
        if node_value:
            node = doc.createElement('policy-file')
            node.appendChild(doc.createTextNode(node_value))
            result.appendChild(node)
    return result

def makeCompilerNode(compiler_args, doc):
    result = doc.createElement('compiler')
    props = ['accessible', 'allow-source-path-overlap',
             'show-actionscript-warnings', 'debug',
             'keep-generated-actionscript', 'optimize',
             'omit-trace-statements', 'show-binding-warnings',
             'show-unused-type-selector-warnings',
             'strict', 'as3', 'es', 'verbose-stack-trace',
             'warn-array-tostring-changes',
             'warn-assignment-within-conditional',
             'warn-bad-array-cast',
             'warn-bad-bool-assignment',
             'warn-bad-date-cast',
             'warn-bad-es3-type-method',
             'warn-bad-es3-type-prop',
             'warn-bad-nan-comparison',
             'warn-bad-null-assignment',
             'warn-bad-null-comparison',
             'warn-bad-undefined-comparison',
             'warn-boolean-constructor-with-no-args',
             'warn-changes-in-resolve',
             'warn-class-is-sealed',
             'warn-const-not-initialized',
             'warn-constructor-returns-value',
             'warn-deprecated-event-handler-error',
             'warn-deprecated-function-error',
             'warn-deprecated-property-error',
             'warn-duplicate-argument-names',
             'warn-duplicate-variable-def',
             'warn-for-var-in-changes',
             'warn-import-hides-class',
             'warn-instance-of-changes',
             'warn-internal-error',
             'warn-level-not-supported',
             'warn-missing-namespace-decl',
             'warn-negative-uint-literal',
             'warn-no-constructor',
             'warn-no-explicit-super-call-in-constructor',
             'warn-no-type-decl',
             'warn-number-from-string-changes',
             'warn-scoping-change-in-this',
             'warn-slot-text-field-addition',
             'warn-unlikely-function-value',
             'warn-xml-class-has-changed']
    node = None
    for p, v in compiler_args.iteritems():
        if p in props:
            node = doc.createElement(p)
            node.appendChild(doc.createElement(compiler_args.get(p)))
            result.appendChild(node)
        elif p == 'source-path':
            node = doc.createElement(p)
            for sp in v:
                pat_element = doc.createElement('path-element')
                pat_element.appendChild(doc.createTextNode(sp))
                node.appendChild(pat_element)
            result.appendChild(node)
        elif p == 'locale':
            node = doc.createElement(p)
            for el in v:
                locale_element = doc.createElement('locale-element')
                locale_element.appendChild(doc.createTextNode(el))
                node.appendChild(locale_element)
            result.appendChild(node)
        elif p == 'external-library-path':
            pass
        elif p == 'library-path':
            pass
        elif p == 'namespaces':
            pass
        elif p == 'keep-as3-metadata':
            pass
        elif p == 'theme':
            pass
        elif p == 'fonts':
            pass
        else:
            raise UserError('Unrecognized compiler argument (%s)' % str(p))
            
    return result

def generateMxmlcConfig(env, project_config):
    """
    Generates mxmlc-config.xml to be loaded with
    $ mxmlc -load-config+=./mxmlc-config.xml
    in order to transfer all additional parameters to mxmlc
    The convention for pasing compiler arguments is that you may use underscores instead
    of hyphens, they will be converted to hyphens automatically.
    Use lists, whenever there is a group of properties
    which is a sub-group of another property, use dictionary, for example
    'metadata': { 'title' : 'Metadata Title', 'description' : 'Example description' ... }
    """
    project_dir = env.GetLaunchDir()
    flex_options = env['FLASHPARAMS']
    if project_config.get('compiler-args', False):
        normalizeCompierOptions(project_config['compiler-args'], flex_options)
    src_folder = project_config['src-folder']
    main_app = project_config['main-app']
    # for now assume there's only one
    flex_options['output'] = project_config['target']
    # also need to parse project_config['compiler-options'] here
    # also need a function for translating from short options to long options
    comp = flex_options.get('compiler', {})
    # bueh, need to normalize all options before I get here...
    src_path = comp.get('source-path') or comp.get('source_path', [])
    if comp.has_key('source_path'):
        comp['source-path'] = comp['source_path']
        del comp['source_path']
    full_src_path = os.path.join(project_dir, src_folder)
    if not full_src_path in src_path:
        src_path.append(full_src_path)
        comp['source-path'] = src_path
        flex_options['compiler'] = comp
        print 'source path %s' % comp['source-path']
    
    doc = Document()
    flex_config = doc.createElement('flex-config')
    node = None
    doc.appendChild(flex_config)
    replaced = None
    simple_options = ['swf-version', 'target-player', 'remove-unused-rsls',
                      'static-link-runtime-shared-libraries', 'compute-digest',
                      'use-network', 'verbose-stack-trace', 'benchmark',
                      'debug-password', 'default-background-color',
                      'default-frame-rate', 'directory', 'dump-config',
                      'output', 'raw-metadata', 'warnings']
    
    for p, v in flex_options.iteritems():
        replaced = p.replace('_', '-')
        if replaced == 'compiler':
            flex_config.appendChild(makeCompilerNode(v, doc))
            # listContains(simple_options, replaced):
        elif replaced in simple_options:
            print 'ever got here?'
            node = doc.createElement(replaced)
            node.appendChild(doc.createTextNode(str(v)))
            flex_config.appendChild(node)
        elif replaced == 'runtime-shared-libraries':
            node = doc.createElement('runtime-shared-libraries')
            url = None
            for lib in rsls_libs:
                url = doc.createElement('url')
                url.appendChild(doc.createElement(lib))
                node.appendChild(url)
        elif replaced == 'rsls-paths':
            for p in rsls_path:
                flex_config.appendChild(makeSharedLibPath(p, doc))
        elif replaced == 'default-size':
            # need to see how it is actually written
            pass
        elif replaced == 'externs':
            # not doing it for now
            pass
        elif replaced == 'frames':
            # important, but not yet...
            pass
        elif replaced == 'resource-bundle-list':
            # important, but not yet...
            pass
        elif replaced == 'include-classes':
            # important, compc-specific but not yet...
            pass
        elif replaced == 'include-namespaces':
            # important, compc-specific but not yet...
            pass
        elif replaced == 'include-lookup-only':
            # important, compc-specific but not yet...
            pass
        elif replaced == 'include-file':
            # important, compc-specific but not yet...
            pass
        elif replaced == 'metadata':
            flex_config.appendChild(makeMetadata(v), doc)
        elif replaced == 'licenses':
            flex_config.appendChild(makeLicenses(v), doc)
        else:
            raise UserError("What is it I don't even... (%s)" % replaced)
    config_file = open(os.path.join(project_dir, 'mxmlc-config.xml'), 'w')
    config_file.write(doc.toxml())
    config_file.close()
    print "generated mxmlc-config.xml: %s" % os.path.join(project_dir, 'mxmlc-config.xml')

def locateBinDir(launch_dir, debugging):
    directories = []
    for (dirpath, dirnames, filenames) in os.walk(launch_dir):
        directories = [d for d in dirnames if d.startswith('bin')]
        break
    match = 'debug' if debugging else 'release'
    if directories:
        for d in directories:
            if d.endswith(match):
                return d
        return directories[0]
    return launch_dir

class FlashBuilderBase(BuilderBase):
    """
    An extension needed to run Flash() target with no arguments
    """
    def __init__(self, **overrides):
        # So far it looks like I won't be using nor action, neither executor
        # so, might remove the below too. Will see, maybe there is some
        # way to use Action / Executor somehow...
        print '__init__ %s => %s' % (self, overrides)
        if 'generator' in overrides:
            print 'action created'
            self.action = SCons.Action.CommandGeneratorAction(
                overrides['generator'], {})
            del overrides['generator']
        else:
            self.action = SCons.Action.ActionBase()
        # this looks bad, but Action is a very difficult to use thing...
        # leaving it for later
        # else:
        #     overrides['action'] = SCons.Action.CommandGeneratorAction(
        #         self._generator, {})
        super(FlashBuilderBase, self).__init__(**overrides)
    
    def __call__(self, *args, **kw):
        # Let's make it SIMPLE for once
        # ...
        if args and len(args) > 1 and args[0] is args[1][0]:
            # fml...
            temp = list(args)
            temp.pop(1)
            args = tuple(temp)
        return self._execute(*args, **kw)
    
    def _late_init(self, **kw):
        # We need this to pass constructor arguments after we have created
        # an instance of this builder. Unfortunately, we have to create
        # the builder before we we know what builder to create...
        print 'late init got stuff: %s' % dict(kw)
    
    def _execute(self, env, target, source, overwarn = {}, executor_kw = {}):
        print "executing base builder."
        # 1 // 0
        # it's a pity, but this isn't very useful either...
        # tlist, slist = self._create_nodes(env, target, source)

        # Check for errors with the specified target/source lists.
        # we didn't create any nodes, so no reason to check anything
        # _node_errors(self, env, tlist, slist)

        # The targets are fine, so find or make the appropriate Executor to
        # build this particular list of targets from this particular list of
        # sources.

        # executor = None
        # key = None

        # if self.multi:
        #     try:
        #         executor = tlist[0].get_executor(create = 0)
        #     except (AttributeError, IndexError):
        #         pass
        #     else:
        #         executor.add_sources(slist)

        # if executor is None:
        #     if not self.action:
        #         fmt = "Builder %s must have an action to build %s."
        #         raise UserError(fmt % (self.get_name(env or self.env),
        #                                 list(map(str,tlist))))
        #     key = self.action.batch_key(env or self.env, tlist, slist)
        #     if key:
        #         try:
        #             executor = SCons.Executor.GetBatchExecutor(key)
        #         except KeyError:
        #             pass
        #         else:
        #             executor.add_batch(tlist, slist)

        # if executor is None:
        #     executor = SCons.Executor.Executor(self.action, env, [],
        #                                        tlist, slist, executor_kw)
        #     if key:
        #         SCons.Executor.AddBatchExecutor(key, executor)

        # oops, cannot use this too...
        # Now set up the relevant information in the target Nodes themselves.
        # for t in tlist:
        #     t.cwd = env.fs.getcwd()
        #     t.builder_set(self)
        #     t.env_set(env)
        #     t.add_source(slist)
        #     # t.set_executor(executor)
        #     t.set_explicit(self.is_explicit)

        # return SCons.Node.NodeList(tlist)
        return []

class FlashDevelopBuilder(FlashBuilderBase):
    # self.fd_project_settings = loadFDProject(files)
    pass

class SwcBuilder(FlashBuilderBase):
    pass

class AirBuilder(FlashBuilderBase):
    pass

class FlashDirBuilder(FlashBuilderBase):
    def _execute(self, env, target, source, overwarn = {}, executor_kw = {}):
         # traceback.print_stack()
        print "source: %s, target: %s" % (len(source), len(target))
        print "node.builder: %s" % source[0].builder
        print "captured builder: %s" % builder
        if not SCons.Util.is_List(target):
            target = [target]

        if not SCons.Util.is_List(source):
            source = [source]
        target = target + ([target[-1]] * (len(source) - len(target)))
        result = []
        # This is just a general idea, it's all wrong factually, just so
        # I'd remember what I wanted to do
        for t, s in zip(target, source):
            if isinstance(s, SCons.Node.FS.Base):
                if isinstance(s, SCons.Node.FS.File):
                    b = env.ASFile
                else:
                    b = env.FlashDir
            else:
                if os.path.isfile(s):
                    b = env.ASFile  # or MXMLFile or FlexCSSFile
                elif os.path.isdir(s):
                    b = env.FlashDir
                else:
                    raise UserError("Don\'t know how to compile '%s'" % b)
            print "result: %s" % result
            result.extend(b(t, s, env, **kw))

class SwfBuilder(FlashBuilderBase):
    pass

class FlashBuilderBuilder(FlashBuilderBase):
    """
    Builds Flash Builder projects.
    """
    def __init__(self, **overrides):
        self.source = None
        self.project = None
        super(FlashBuilderBase, self).__init__(**overrides)
        
    def _execute(self, env, target, source, overwarn = {}, executor_kw = {}):
        print 'this is what you get, this is what you get... when you mess with us %s' % [env, target, source]
        if not source:
            source = os.path.join(env.GetLaunchDir(), '.actionScriptProperties')
        self.project = loadFBProject(source)
        print 'was there a target? %s' % target
        print 'was there a env? %s' % env
        # 1 // 0
        # This really has to be more complex, modules, styles, locales etc...
        # This entire block was taken from the initialization function, it
        # may mean it is redundant in the initialization function.
        if not target:
            self.source = os.path.join(
                env.GetLaunchDir(),
                self.project.get('src-folder'),
                self.project.get('main-app'))
            
            debugging = env['FLASHPARAMS'].get('compiler', None)
            if not debugging or (debugging.has_key('debug') and not debugging.debug):
                debugging = False
            try:
                bin_dir = self.project['out-dir']
            except KeyError:
                bin_dir = locateBinDir(env.GetLaunchDir(), debugging)
            if SCons.Util.is_List(self.source):
                target = []
                for s in self.source:
                    target.append(
                        env.GetLaunchDir(),
                        os.path.join(
                            bin_dir,
                            os.path.basename(s).split('.')[0] + '.swf'))
            else:
                target = os.path.join(
                    env.GetLaunchDir(),
                    bin_dir, os.path.basename(self.source).split('.')[0] + '.swf')
        self.project['target'] = target
        
        print "ever had source? %s" % self.source

        print "executing.... %s" % source
        print "target: %s" % target
        generateMxmlcConfig(env, self.project)
        # OK, now it looks like it would make sense to use Action here to
        # run mxmlc
        print "which mxmlc? %s" % env['MXMLC']

        process = subprocess.Popen(
            [env['MXMLC'],
             '-load-config+=' + os.path.join(
                 env.GetLaunchDir(), 'mxmlc-config.xml'), self.source],
            stdout = subprocess.PIPE, stderr = subprocess.PIPE)
        output, errors = process.communicate()
        print 'mxmlc output: %s' % output
        print 'mxmlc errors: %s' % errors

        # It looks like we rather need to execute the base builder before
        # we start doing anything in this function, not after
        return super(FlashBuilderBuilder, self)._execute(
            env, target, source, overwarn, executor_kw)

def FlashBuilder(**kw):
    """
    Perhaps temporary, replacement for the original Builder
    from SCons.Builder. This is needed to allow no arguments call to
    Flash()
    """
    composite = None
    if 'generator' in kw:
        if 'action' in kw:
            raise UserError("You must not specify both an action and a generator.")
    elif 'action' in kw:
        source_ext_match = kw.get('source_ext_match', 1)
        if 'source_ext_match' in kw:
            del kw['source_ext_match']
        if SCons.Util.is_Dict(kw['action']):
            composite = DictCmdGenerator(kw['action'], source_ext_match)
            kw['action'] = SCons.Action.CommandGeneratorAction(composite, {})
            kw['src_suffix'] = composite.src_suffixes()
        else:
            kw['action'] = SCons.Action.Action(kw['action'])

    if 'emitter' in kw:
        emitter = kw['emitter']
        if SCons.Util.is_String(emitter):
            # This allows users to pass in an Environment
            # variable reference (like "$FOO") as an emitter.
            # We will look in that Environment variable for
            # a callable to use as the actual emitter.
            var = SCons.Util.get_environment_var(emitter)
            if not var:
                raise UserError("Supplied emitter '%s' does not appear " +
                                "to refer to an Environment variable" % emitter)
            kw['emitter'] = EmitterProxy(var)
        elif SCons.Util.is_Dict(emitter):
            kw['emitter'] = DictEmitter(emitter)
        elif SCons.Util.is_List(emitter):
            kw['emitter'] = ListEmitter(emitter)
    source = kw.get('source', None)
    target = kw.get('target', None)
    result = None
    if not source:
        env = kw['env']
        source = env.GetLaunchDir()
        print "target acquired %s" % source
        files = None
        directories = None
        for (dirpath, dirnames, filenames) in os.walk(source):
            files = filenames
            directories = dirnames
            break
        if '.actionScriptProperties' in files:
            # A Flash Builder project, will try to use its settings
            # Need to return sooner a special FlashBuilder builder
            source = os.path.join(source, '.actionScriptProperties')
            print "Flash Builder project detected"
            result = env.FlashBuilder
            
        elif hasFlashDevelopProjects(files):
            # A FlashDevelop projec(s) detected
            # Need to return sooner a special FlashDevelop builder
            print "FlashDevelop project detected"
            result = env.FlashDevelop
            
        else:
            maybeSource = subDirsContainFiles(
                source,
                ['.', 'src', 'sources', 'flash', 'classes'],
                ['Main.as', 'Main.mxml', 'Main.css'])
            if maybeSource:
                result = env.Swf
                source = maybeSource

    print "source found %s, builder %s and target %s" % (source, result, target)
    if not result:
        raise UserError("Couldn't determine what builder to use")
    # RFC-1925 (6a) (corollary).
    # It is always possible to add another level of indirection.
    # I think I'm starting to suspect something... what if at this point
    # the sources aren't given yet? Maybe it's only when we __call__()
    # the builder, i.e. I need to put all this initialization into
    # _execute()?.. Or, maybe not. The user would've specified a particular
    # builder. Sounds like a plan...
    # result.method._late_init(**kw)
    if not composite is None:
        result = CompositeBuilder(result, composite)

    return result

def _detect(env):
    return env.Detect("mxmlc") or os.environ.get('FLEX_HOME', None) or env['ENV'].get('FLEX_HOME', None)

def makeUtilityPath(env, flex_home, utility):
    return env.Detect(utility) or os.path.join(flex_home, 'bin', utility)

def generate(env):
    """
    Add Builders and construction variables for Flash to an Environment.
    """
    # 1 // 0
    flex_home = ARGUMENTS.get('FLEX_HOME') or os.environ.get('FLEX_HOME', None)
    flex_home = flex_home or env['ENV'].get('FLEX_HOME', None)
    # looks like we are adding to the wrong place :/
    # maybe it all has to go into env['ENV']?
    env.PrependUnique(FLEX_HOME = flex_home)
    flex_home = flex_home or ''
    env.PrependUnique(MXMLC = makeUtilityPath(env, flex_home, 'mxmlc'))
    env.PrependUnique(COMPC = makeUtilityPath(env, flex_home, 'compc'))
    env.PrependUnique(ADT = makeUtilityPath(env, flex_home, 'adt'))
    env.PrependUnique(ASDOC = makeUtilityPath(env, flex_home, 'asdoc'))
    env.PrependUnique(FLASHSUFFICES = ['.as', '.mxml', '.css'])
    env.PrependUnique(FLASHBINSUFFICES = ['.swf', '.swc', '.air'])
    env.PrependUnique(FLASHPARAMS = {'static-link-runtime-shared-libraries': True})
    
    Swf = SwfBuilder(
        src_suffix = ['.as', '.mxml', '.css'],
        single_source = True)

    Swc = SwcBuilder(
        src_suffix = ['.as', '.mxml', '.css', '.xml'],
        single_source = False)
    
    Air = AirBuilder(src_suffix = '.xml', single_source = False)

    FlashDir = FlashDirBuilder(single_source = True)
    
    FlexBuilder = FlashBuilderBuilder(
        src_suffix = '.actionScriptProperties',
        single_source = True)

    FlashDevelop = FlashDevelopBuilder(
        src_suffix = '.as3proj',
        single_source = False)
    
    env.Append(BUILDERS =
               {'Swf' : Swf, 'Swc' : Swc, 'FlashDir' : FlashDir,
                'FlashDevelop' : FlashDevelop,
                'FlashBuilder' : FlexBuilder, 'Air' : Air})
    
    # This is the "smart" builder (well, as smart as it can be...)
    # it will try to decide which builder is appropriate to use, if the
    # users couldn't decide themselves :|
    Flash = FlashBuilder(
        env = env,
        src_suffix = ['.css', '.as', '.mxml', '.actionScriptProperties', '.as3proj'],
        single_source = False)
    env.Append(BUILDERS = {'Flash' : Flash})
    
def exists(env):
    return _detect(env)
